-
-
Notifications
You must be signed in to change notification settings - Fork 148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Validate Checkpoint in ChangePack for PushPull API requests #959
base: main
Are you sure you want to change the base?
Conversation
…am#952) Added the handler to allow health checks to be performed with plain HTTP GET requests needed for traditional uptime checker or load balancer, along with existing gRPC health check.
…, non-sequential ClientSeq, ClientSeq greater than ClientInfo's ClientSeq, and ServerSeq greater than DocInfo's ServerSeq.
…, non-sequential ClientSeq, ClientSeq greater than ClientInfo's ClientSeq, and ServerSeq greater than DocInfo's ServerSeq.
WalkthroughThe updates improve error handling for client sequences in request packs, enhancing data integrity during synchronization. New error variables were introduced for non-sequential client IDs, and the Changes
Assessment against linked issues
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (3)
- server/packs/packs.go (4 hunks)
- server/packs/packs_test.go (1 hunks)
- server/rpc/connecthelper/status.go (2 hunks)
Additional comments not posted (9)
server/packs/packs.go (3)
42-43
: LGTM: Error variable declaration.The error variable
ErrClientSeqNotSequential
is appropriately defined for handling non-sequential client sequences.
287-306
: LGTM: Client sequence validation function.The
validateClientSeqSequential
function is well-implemented, ensuring client sequences are sequential and providing clear error messages.
86-89
: LGTM: Integration of client sequence validation.The
PushPull
function now validates client sequences, which enhances synchronization logic robustness.Ensure that this validation does not introduce any unintended side effects.
Verification successful
Integration of
validateClientSeqSequential
is isolated toPushPull
.The
validateClientSeqSequential
function is used exclusively in thePushPull
method, ensuring it does not introduce unintended side effects elsewhere in the codebase. The integration appears robust and focused on enhancing synchronization logic.
- Location:
server/packs/packs.go
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the usage of `validateClientSeqSequential` in the codebase. # Test: Search for the function usage. Expect: Only occurrences of the new usage. rg --type go -A 5 $'validateClientSeqSequential'Length of output: 594
server/rpc/connecthelper/status.go (2)
51-51
: LGTM: Error to connect code mapping.The mapping of
packs.ErrClientSeqNotSequential
toconnect.CodeInvalidArgument
is appropriate for indicating malformed requests.
104-104
: LGTM: Error to string code mapping.The mapping of
packs.ErrClientSeqNotSequential
to "ErrClientSeqNotSequential" provides a clear string representation for error handling.server/packs/packs_test.go (4)
47-76
: LGTM: Test for sequential client sequences (happy case).The test case
RunPushPullWithSequentialClientSeqTest
is well-structured and verifies that valid sequential client sequences are processed correctly.
78-107
: LGTM: Test for non-sequential client sequences.The test case
RunPushPullWithNotSequentialClientSeqTest
correctly asserts that an error is returned for non-sequential client sequences.
109-148
: LGTM: Test for duplicate client sequences.The test case
RunPushPullWithClientSeqLessThanClientInfoTest
ensures that duplicate client sequences do not cause errors, verifying validation robustness.
150-187
: LGTM: Test for server sequence validation.The test case
RunPushPullWithServerSeqGreaterThanDocInfoTest
correctly asserts an error is returned when the server sequence exceeds expectations, ensuring proper validation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answers to your questions
1. Case 1: The function validateClientSeqSequential() verifies that the Changes in the request are sequential. It does not validate whether the first Change's ClientSeq differs by 1 from the ClientSeq in the existing ClientInfo. Should this be added?
I think it is necessary. The Yorkie server needs to ensure that changes are not missing and are causally ordered. Consider the following example:
If a server's ClientSeq
is 1
and a client sends a change with ClientSeq
3
, the server must check if a change with ClientSeq
2
is missing and reject the request, because the change with ClientSeq
3
may causally depend on the one with ClientSeq
2
.
2. Case 1: Should we remove the TODO comment in the PushPull function in server/packs/packs.go?
I think so.
3. Case 3: The issue specifies that malicious ServerSeqs should return connect.CodeInvalidArgument, but currently, it returns connect.CodeFailedPrecondition. Is this appropriate?
According to the current code,
InvalidArgument
means the request is malformed.FailedPrecondition
means the request is rejected because the state of the system is not the desired state.
Receiving a malicious ServerSeq
seems more related to the system's state than to value formatting, so I think FailedPrecondition
is more appropriate.
4. If client actions are required in response to these scenarios, we should discuss how to handle them.
I agree. We can continue this discussion in a follow-up issue.
Additional Consideration
According to the suggestion regarding exception handling, it may be necessary to skip these validation checks and return ok
if the requests are duplicates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Outside diff range, codebase verification and nitpick comments (1)
server/packs/packs.go (1)
88-88
: Review the necessity of the TODO comment.The newly implemented validation functions may address the concerns mentioned in the TODO comment about change ordering.
- // TODO: Changes may be reordered or missing during communication on the network. - // We should check the change.pack with checkpoint to make sure the changes are in the correct order. + // Validation ensures changes are in the correct order with respect to the checkpoint.
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- server/packs/packs.go (4 hunks)
- server/rpc/connecthelper/status.go (2 hunks)
Files skipped from review as they are similar to previous changes (1)
- server/rpc/connecthelper/status.go
Additional comments not posted (5)
server/packs/packs.go (5)
41-47
: Error variable declarations look good.The error variables improve the clarity and specificity of error handling for client sequence validation.
291-301
: FunctionvalidateClientSeqSequential
is well-implemented.The function effectively orchestrates the validation of client sequences with a modular approach.
303-316
: FunctionvalidateClientSeqSequentialWithCheckpoint
is correctly implemented.The function accurately checks the sequence against the checkpoint and provides a clear error message.
318-333
: FunctionvalidateClientSeqInChangesAreSequential
is well-implemented.The function accurately validates the sequential order of client sequences in changes.
89-93
: Integration of client sequence validation inPushPull
is appropriate.The addition of
validateClientSeqSequential
enhances the robustness of the synchronization logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Outside diff range, codebase verification and nitpick comments (1)
server/packs/packs.go (1)
302-314
: Clarify error message for sequential validation.The error message in
validateClientSeqSequentialWithCheckpoint
could be more descriptive to help diagnose issues more effectively. Consider specifying the expected and actual sequences in more detail.- "ClientSeq is not sequential with DocInfo.Checkpoint.ClientSeq (expected: %d, actual: %d) : %w", + "ClientSeq mismatch: expected sequential order with Checkpoint.ClientSeq (expected: %d, found: %d) : %w",
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- server/packs/packs.go (4 hunks)
- server/packs/packs_test.go (1 hunks)
Files skipped from review due to trivial changes (1)
- server/packs/packs_test.go
Additional comments not posted (3)
server/packs/packs.go (3)
317-331
: LGTM! Sequential validation logic is clear and correct.The
validateClientSeqInChangesAreSequential
function is well-implemented. The logic is straightforward, and the error message is clear.
290-300
: Verify integration and usage of validation function.The
validateClientSeqSequential
function is well-structured and delegates tasks to helper functions. Verify its integration and usage within the codebase to ensure it functions as intended.
88-92
: Verify error handling and control flow.The integration of
validateClientSeqSequential
in thePushPull
function appears correct. Ensure that the error handling and control flow align with the intended logic, particularly regarding the response codes for validation failures.Verification successful
Error Handling in PushPull Function Verified
The error handling in the
PushPull
function is correctly implemented, returningnil, err
whenvalidateClientSeqSequential
encounters an error. This follows standard Go practices for error handling.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the error handling and response codes in the PushPull function. # Test: Search for error handling logic in PushPull function. Expect: Proper response codes and error messages. rg --type go -A 5 'return nil, err' --context 10Length of output: 225025
Script:
#!/bin/bash # Description: Search for the PushPull function definition and its error handling in server/packs/packs.go. # Test: Locate the PushPull function and analyze its error handling. ast-grep --lang go --pattern $'func PushPull($_, $_, $_, $_, $_, $_, $_) ($_ , error) { $$$ return nil, err $$$ }' server/packs/packs.goLength of output: 143
Script:
#!/bin/bash # Description: Locate the PushPull function and its error handling in server/packs/packs.go. # Test: Search for the PushPull function definition and its error handling. rg --type go -A 20 'func PushPull' server/packs/packs.goLength of output: 1274
hi @sejongk here is the description of additional codes 1. add validation
|
…n the case of `ErrClientSeqNotSequentialWithCheckpoint`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- server/rpc/connecthelper/status.go (3 hunks)
Files skipped from review as they are similar to previous changes (1)
- server/rpc/connecthelper/status.go
I changed it to return the Why?@sejongk asked me whether it is appropriate for both so, I read the gRPC Core - Status codes and their use in gRPC documentation. and Here is an excerpt from the document:
The difference lies in whether or not it is related to the system's status.After reading the document,
As I understand it:
The examples provided in the document align with this understanding:
In the case of On the other hand, the requirement that "ClientSeqs within Changes must be sequential" in ConclusionTherefore, I believe it is better for these two situations to return different status codes:
|
1. What this PR does / why we need it:
This issue is validation of the Checkpoint when a client calls the PushPull API and sends a request ChangePack. This validation is necessary to handle potential issues such as network-induced duplicate requests, bugs in the new SDK, or malicious tampering.
Specifically, three scenarios needed to be validated:
I will refer to these three scenarios as Case 1, 2, 3, 4
Upon reviewing the code, I found that Cases 3 and 4 were already implemented.
Thus, I only needed to implement Case 1. However, I have written test codes for Cases 1, 2, 3, 4 and the happy case. I will explain the implementation for each case and the corresponding test code.
In the upcoming code blocks, the first line of each comment will indicate the file name and function name for your reference.
I also have a few questions. These questions are marked in bold and will be included in the
Special notes for your reviewer
section.1.1
Case 1
: The ClientSeqs in the request sent to the PushPull API are sequentialThis validation is implemented in
packs.go
and occurs immediately when the PushPull function is called.This function checks if the Changes in the request are sequential.
Q. It does not validate whether the first Change's ClientSeq differs by 1 from the ClientSeq in the existing ClientInfo. Should this be added? Logically, if there are 0 or 1 changes, there's nothing to validate, so it returns without issues. If the sequence is not sequential, it returns
ErrClientSeqNotSequential
, which is mapped toconnect.CodeInvalidArgument
in the response viaerrorToConnectCode
.This validation is tested at the location of the above comment.
Q. Should we now remove this TODO comment?
1.2 `Case 2: The ClientSeq in changes not sequential with DocInfo.Checkpoint.ClientSeq
these two case return
connect.CodeInvalidArgument
1.3
Case 3
: The ClientSeq in changes cannot be less than the ClientSeq in the Server's ClientInfoThis was already implemented, and it simply logs a warning. It returns OK as specified in the current issue.
1.4
Case 4
: The ServerSeq in the request cannot be greater than the ServerSeq maintained by the server.This was also already implemented. In the code below,
initialServerSeq
refers to the ServerSeq maintained in the Document Info.Q. Currently, it returns
connect.CodeFailedPrecondition
instead ofconnect.CodeInvalidArgument
as specified in the issue. Is this appropriate?1.5 Test Code
The test code is located in
server/packs/packs_test.go
. It covers the following four scenarios:happy case
: Sequential ClientSeq in Client Requestduplicated request
: ClientSeq in Request is less than ClientInfo's ClientSeq (But No Error)2. Which issue(s) this PR fixes:
resolves #805
3. Special notes for your reviewer:
Questions 1 through 3 are addressed in the case descriptions above:
Case 1
: The functionvalidateClientSeqSequential()
verifies that the Changes in the request are sequential. It does not validate whether the first Change's ClientSeq differs by 1 from the ClientSeq in the existing ClientInfo. Should this be added?Case 1
: Should we remove the TODO comment in the PushPull function inserver/packs/packs.go
?Case 3
: The issue specifies that malicious ServerSeqs should returnconnect.CodeInvalidArgument
, but currently, it returnsconnect.CodeFailedPrecondition
. Is this appropriate?If client actions are required in response to these scenarios, we should discuss how to handle them.
4. Does this PR introduce a user-facing change?:
5. Additional documentation:
Here, I have attached the questions I posted on the issue and the answers I found. I hope this will be helpful to those studying ClientSeq and ServerSeq. If there are any mistakes, please let me know.
I have also attached the responses from @sejongk to my questions. I learned a lot from his answers, and I am very grateful to him.
5.1 My Question And Answer
Hello,
I have a few questions as I work on understanding the content to resolve the issue.
Q1. Where do the server's
ClientSeq
andServerSeq
come from?To resolve this issue, I thought it would be sufficient to compare the
ClientSeq
andServerSeq
of the client with those maintained by the server.Is it correct that the server's
ClientSeq
andServerSeq
can be obtained from the Checkpoint ofClientInfo
, which is derived from the ClientId in the Request Message?Q2. Where do the
ClientSeq
andServerSeq
sent by the client come from?The ChangePack in the Request's Msg contains a Checkpoint and an array of Changes.
The Checkpoint holds its
ClientSeq
andServerSeq
, and each Change in the array also has itsClientSeq
andServerSeq
.According to the issue, it mentions "so Change.ID.Checkpoint.ClientSeq should increment sequentially by one." Does this mean that the
ClientSeq
of the client should be the sequential values of each Change, and these should be validated against the server'sClientSeq
(rather than theClientSeq
in the Checkpoint of the ChangePack)?And regarding "Checkpoint.ServerSeq in the request ChangePack for PushPull API cannot be greater than the server's Checkpoint.ServerSeq since it is set when the server saves the Change to the database."
Should the client's
ServerSeq
be obtained from theServerSeq
in the Checkpoint of the ChangePack in the Msg (not theServerSeq
of each individual Change)?Answer 1, 2
ServerSeq
can be found in DocInfo. Since theServerSeq
is updated every time a Change from various clients is applied, it is managed at the Document level.ClientSeq
can be found in the Checkpoint of ClientInfo. Since the server needs to know the lastClientSeq
sent by the client, it is managed at the ClientInfo level.ClientSeq
is assigned by the client for each Change and is sequentially included in Changes. TheClientSeq
can be found in both the Checkpoint and the Changes.ServerSeq
can be found inreqPack.Checkpoint.ServerSeq
. The client retains the lastServerSeq
received during synchronization and includes it in the Checkpoint when sending a Request.Q3. How is a "wrong" ClientSeq determined?
You mentioned that
Change.ID.Checkpoint.ClientSeq should increment sequentially by one.
Does this mean that the following scenarios are considered wrong?ClientSeq
of each Change in the ChangePack sent by the client is not sequential, the request is wrong.ClientSeq
in the CheckPoint sent by the client is ≤ the_ClientSeq
inclientInfo
, it is wrong.Answer 3
Q4. How is a duplicate request determined?
Would it be correct to consider the scenario where the
ClientSeq
sent by the client is equal to the_ClientSeq
inclientInfo
as a duplicate request due to network delay? I seem to be missing the criteria for determining a "duplicate request."Answer 4
Answered in Answer 3.
Q5. How is a wrong ServerSeq determined?
Is it wrong when the
ServerSeq
sent by the client is greater than theServerSeq
obtained fromClientInfo
on the server?Answer 5
If the
ServerSeq
sent by the client is greater than theServerSeq
found in DocInfo on the server, it is a malicious request. This is because theServerSeq
held by the client is initially provided by the server. Since theServerSeq
increases sequentially, the client'sServerSeq
should never be larger.Q6. Where should the validation take place?
I believe it should occur in the
PushPullChanges
function inyorkie_server.go
. Should I declare the validate function and error directly inyorkie_server.go
, or should I create a validator file in therpc
package and declare the validate function and error there?Answer 6
I was curious whether a separate object or layer was needed for validation. Based on Yorkie's existing validation methods, it seems unnecessary.
5.2
sejongk
's AnswerUnderstanding the concept of a Checkpoint seems crucial for this issue. I searched for relevant design documents and found a document on GC garbage-collection.md , though it might be good to add more detailed information about Checkpoints there or even create a separate Checkpoint document in the future.
You may already be familiar with Checkpoints, but I'll share what I reviewed today:
server_seq
, which is incremented by 1. ThedocInfo.SeverSeq
corresponds to this global counter.server_seq
that has been incremented up to that point. Thisserver_seq
serves to inform the client of how far it has received the Changes.server_seq
it previously received to the server inreqPack.Checkpoint.ServerSeq
, notifying the server of how far it has received and prompting the server to send the Changes corresponding to the subsequentserver_seq
.client_seq
on the client side asclientInfo.Checkpoint(docInfo.ID).ClientSeq
, which is used to validate theclient_seq
of the Changes.client_seq
within the ChangePack must be sorted and incremented by 1 sequentially. This is because if a Change is lost due to network issues, it can be detected through theclient_seq
. This is similar to how sequence numbers are assigned to each packet in TCP.client_seq
is smaller than theclient_seq
the server holds (ifcn.ID().ClientSeq() > cp.ClientSeq
).min_synced_seq
is one of the core elements in GC.After reviewing the above, it would be helpful to look at the diagrams explaining the Checkpoint flow at the following links:
You may want to review the code in these files:
server/packs/packs.go/func PushPull
server/packs/pushpull.go/func pushChanges
server/packs/pushpull.go/func pullChanges
6. Checklist:
Summary by CodeRabbit
New Features
Bug Fixes
Tests